﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using Silvermoon.Controls;
using System.Drawing;
using Silvermoon.Windows;

namespace Passwords.Controllers
{

    /// <summary>
    /// Abstract class of a  controller  which exchanges data between view and model and handles commands raised on the view.
    /// See also MCP design pattern "supervising controller" and "passive view". Silvermoon prefers passive view pattern over supervising controller
    /// since this pattern discoples the model from the view.
    /// </summary>
    /// <typeparam name="V">View.</typeparam>
    /// <typeparam name="M">Model.</typeparam>
    public abstract class Controller<V, M> where V : Control
    {
        #region fields

        private M model;
        private V view;
        private Dictionary<Command, EventHandler<CommandEventArgs>> registeredCommands;

        #endregion
        #region ctor


        public Controller()
            : base()
        {
            RegisterCommands();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModel&lt;V, M&gt;"/> class.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="model">The model.</param>
        public Controller(V view, M model)
            : this()
        {
            Initialize(view, model);
        }
       

        #endregion
        #region props

        /// <summary>
        /// Gets or sets the model associated with the view.
        /// </summary>
        public M Model
        {
            get { return model; }
            set
            {
                if (!Object.Equals(model, value))
                {
                    M oldModel = model;
                    model = value;
                    OnModelChanged(oldModel);
                }
            }
        }

        /// <summary>
        /// Gets the view which is controlled.
        /// </summary>
        public V View
        {
            get { return view; }
            private set
            {
                if (view != value)
                {
                    V oldView = view;
                    view = value;
                    OnViewChanged(oldView);
                }
            }
        }

        #endregion
        #region methods

        public void RegisterCommand(Command command, EventHandler<CommandEventArgs> action)
        {
            if (registeredCommands == null) registeredCommands = new Dictionary<Command, EventHandler<CommandEventArgs>>();
            registeredCommands[command] = action;
        }

        public void UnregisterCommand(Command command)
        {
            if (registeredCommands != null)
            {
                registeredCommands.Remove(command);
            }
        }

        /// <summary>
        /// Registers all commands to be recogized.
        /// </summary>
        protected virtual void RegisterCommands()
        {
        }

        private void Initialize(V view, M model)
        {
            this.View = view;
            this.Model = model;
            Initialize();
        }


        private void OnViewChanged(V oldView)
        {
            if (oldView != null)
            {
                oldView.Disposed -= OnViewDisposed;
                UninitializeView(oldView);
            }
            if (view != null)
            {
                view.Disposed += new EventHandler(OnViewDisposed);
                if (registeredCommands != null) view.CommandRaised += OnCommandRaised;
                InitializeView(view);
            }

        }

        private void OnViewDisposed(object sender, EventArgs e)
        {
            view.CommandRaised -= OnCommandRaised;
            View = null;
            Model = default(M);

        }

        private void OnModelPropertyChangedInternal(object sender, PropertyChangedEventArgs e)
        {
            OnModelPropertyChanged(e.PropertyName);
        }

        /// <summary>
        /// Occurs when the Model property changes.
        /// </summary>
        /// <param name="oldModel">Old value of Model property.</param>
        protected virtual void OnModelChanged(M oldModel)
        {
            INotifyPropertyChanged notify;
            notify = oldModel as INotifyPropertyChanged;

            notify = model as INotifyPropertyChanged;
            if (notify != null) notify.PropertyChanged += new PropertyChangedEventHandler(OnModelPropertyChangedInternal);

            if (View!=null) UpdateView();
        }


        /// <summary>
        /// Occurs when a property of the model has changed if model implements INotifyPropertyChanged.
        /// </summary>
        /// <param name="propertyName">Name of the property of the model that has changed.</param>
        protected virtual void OnModelPropertyChanged(string propertyName)
        {
        }

        /// <summary>
        /// Initializes the ViewModel after view and model are set by the constructor.
        /// </summary>
        protected virtual void Initialize() { }

        /// <summary>
        /// Updates the view with data from the model.
        /// </summary>
        public abstract void UpdateView();

        /// <summary>
        /// Updates the model with values from the view.
        /// </summary>
        public virtual void UpdateModel()
        {
        }

        /// <summary>
        /// Initializes a view.
        /// This is the place to add event handlers to the view for controlling purpose.
        /// </summary>
        /// <param name="view">View to initializes.</param>
        protected virtual void InitializeView(V view)
        {
        }

        private void OnCommandRaised(object sender, CommandEventArgs e)
        {
            Command command = e.Command as Command;
            if (command != null)
            {
                EventHandler<CommandEventArgs> handler;
                if (registeredCommands.TryGetValue(command, out handler))
                {
                    handler(sender, e);
                }
            }
        }

        /// <summary>
        /// Uninitializes a view
        /// This is the place to remove event handlers from the view which where previously added on <see cref="InitView"/>.
        /// </summary>
        /// <param name="view">View to uninitilize.</param>
        protected virtual void UninitializeView(V view)
        {
        }

        #endregion
    }
}
